#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;
volatile int flag =1;
void hander(int sig){//自定义方法
    cout<<"接收到信号： "<<sig<<endl;
    while(flag){
        cout<<"sig:: "<<sig<<"pid: "<<getpid()<<endl;
        sleep(1);
    }
    //cout<<sig<<"退出 flag "<<flag<<"-> 1"<<endl;
    flag=1;
}
void break_hander(int sig){//利用三号进程 打破循环或者退出 
        cout<<"3"<<endl;
        sigset_t penging_set;
        sigemptyset(&penging_set);
        sigpending(&penging_set);
        int cnt =0;
        for(int i=1;i<=31;i++){
            if(sigismember(&penging_set,i))
                ++cnt;
        }
        cout<<"还剩 "<<cnt<<"个信号未决";
        if(sigisemptyset(&penging_set))
            exit(0);
        //cout<<"flag"<<flag<<"->0"<<endl;
        flag=0;
}


int main(){
    //PCB中 有三张表（不同的环境可能实现方式不一样） block（也许是位图） pending（也许是位图） hander（也许是函数指针）
    //信号产生就是 将pending 表中对应信号位置 设定为1
    //当进程从内核态 回到用户态 如时间片回复 或者 调用系统接口返回会遍历检查pending 如果为1的同时，block 不为1（默认情况）则会直接调用hander的方法
    //如果hander为SIG_IGN就啥也不干 把pending变回0 为SIG_DFL则默认行为分为（DUMP TERM ...）
    //如果是自定义行为则 切换为用户态执行自定义方法后再切回内核态继续返回。
    //内核态则是进程地址空间中内核的内核空间，
        //内核空间对应内核页表（所有进程共享，但是不同进程也有权限区别）指向内核的代码和数据，
        //来进行如系统调用或时间片到了 指向系统方法切换进程。
        //具体体现或主要是 时间片到了切成内核 然后再次被切回来时 或者系统调用返回时 
    
    //sigset_t 类型就是block 表和pending表的类型或理解为对上层的封装
    sigset_t myset;
    sigemptyset(&myset);//所有信号对应位置全部设置为0
    sigfillset(&myset);//所有信号对应位置全部设置为1
    sigaddset(&myset,1);//set指定信号 跟位图操作一样 重复多少次也是1
    sigemptyset(&myset);
    //cout<<"signo_1 ::" <<sigismember(&myset,1)<<endl;//test return 1/0;
    //sigfillset(&myset);
    //sigset_t 配合如下几个接口可以对信号产生到处理做操作。也就是对这三张表做操作
    //1.sigprocmask 可以往block表中添加SIG_BLOCK 删除SIG_UNBLOCK  或者直接覆盖成自己的表 SIG_SETMASK
    //sigprocmask(int how,const sigset_t* set,sigset_t* old) 
    //sigprocmask(SIG_BLOCK,&myset,nullptr);
    sigset_t getset;
    sigemptyset(&getset);
    //signal 设置i信号的自定义方法(更改hander表)
    for(int i=1;i<=31;i++)
        signal(i,hander);
    //sigaction signal 的升级版 对信号对应的hander做操作 这里我们只管mask其他参数不管（设为0）
    //在处理信号的时候 会自动block它 等处理完了在自动接触block，否则时间片一到，等切回来就同样的信号打断然后来不及处理，
    //但是处理过程中还是会被其他的信号打断，处理完新的信号再回来继续处理当前信号
    //这个升级版可以通过mask信号级添加这个hander处理中途屏蔽的信号
    struct sigaction newac1,newac2,oldac;//这个类型相当于对hander方法的详细封装
    newac1.sa_flags=newac2.sa_flags=0;
    newac1.sa_restorer=newac2.sa_restorer=nullptr;
    newac1.sa_handler=hander;
    newac2.sa_handler=break_hander;
    sigfillset(&newac1.sa_mask);//除管理员信号否则无法打断 其他信号都会被屏蔽
    sigfillset(&newac2.sa_mask);
    sigdelset(&(newac1.sa_mask),3);
    //只有三号进程可以中断循环
    for(int i=1;i<=31;i++)
        sigaction(i,&newac1,nullptr);//可以不获取
    sigaction(3,&newac2,&oldac);//可以获取之前的hander

    //sigpending 获取pending表的拷贝 成功返回0 失败-1
    //如果没有设置block 肯定全是0 因为来不及看到就被递达了（被hander handle掉了）
    //只有未决(产生还没被handle，包括被阻塞)的信号才能被观测到 阻塞（BLOCK）就是使信号一直处于未决状态。
    while(!sigpending(&getset)){
        for(int i=1;i<=31;i++)
            cout<<sigismember(&getset,i)?1:0;
        cout<<endl;
        sleep(1);
    }
    //kill（pid,sig）向pid进程发送sig信号
    
    return 0;
}